#include "Server.h"
#include <unistd.h>

#define READ_BLOCK_SIZE 256
#define MAX_SOCK_SIZE 10

std::vector<char> Server::read(int _sock, struct sockaddr_in* src) {
	std::vector<char> output;
	socklen_t len = sizeof(struct sockaddr_in);
	char buf[READ_BLOCK_SIZE+1];
	buf[READ_BLOCK_SIZE] = 0;
	while((result = recvfrom(_sock, buf, READ_BLOCK_SIZE, 0,
			(struct sockaddr*)src, &len)) > 0) {
		for(int i = 0; i < result; i++)
			output.push_back(buf[i]);
		if(result < READ_BLOCK_SIZE)
			break;
	}
	// Check for errors
	if(result < 0) {
		auto_set_error("Error while receiving from client");
		output.clear();
		return output;
	}

	// For testing purposes
	Packet * p = generate_packet(output);
	if(p->get_opcode() == RRQ) {
		RRQ_Packet * rp = (RRQ_Packet *) p;
		printf("Received RRQ file: %s\n", rp->get_filename().c_str());
	}
	else if(p->get_opcode() == ACK) {
		ACK_Packet * ap = (ACK_Packet *) p;
		printf("Received ACK block number: %d\n", ap->get_block_no());
	}
	delete p;

	return output;
}

void Server::send(int _sock, struct sockaddr_in* src, std::vector<char> data) {
	result = sendto(_sock, data.data(), data.size(), 0,
			(struct sockaddr*)src, sizeof(struct sockaddr_in));
	// Check for errors
	if(result < 0) {
		auto_set_error("Error while sending from server");
	}

	// For testing purposes only
	/*Packet * p = generate_packet(data);
	printf("opcode: %d\n", p->get_opcode());
	if(p->get_opcode() == DATA) {
		DATA_Packet* dp = (DATA_Packet*) p;
		printf("Sent DATA block number: %d bytes: %d\n", dp->get_block_no(), dp->get_data().size());
	}
	else if(p->get_opcode() == ERROR) {
		ERROR_Packet* ep = (ERROR_Packet*) p;
		printf("Sent ERROR number: %d message: %s\n", ep->get_ErrorCode(), ep->get_ErrMsg().c_str());
	}
	delete p;*/
}

void Server::stop_server() {
	close(incoming_sock);
}

void FileTransfer::read() {
	std::vector<char> msg = Server::read(sock, &client);
	Packet * pkt = generate_packet(msg);
	if(pkt->get_opcode() == ACK) {
		ACK_Packet* apkt = (ACK_Packet*) pkt;
		ack(apkt->get_block_no());
	}
	else if(pkt->get_opcode() == RRQ) {
		// Hopefully this is at the beginning of the execution
		send_next_block();
	}
	delete pkt;
}

void FileTransfer::get_next_block() {
	current_block.clear();
	char buf[512];
	int bytes = file.readsome(buf, 512);
	printf("Bytes: %d", bytes);
	// read more in case it reaches end of file block
	if(bytes < 512) {
		int bytes2 = file.readsome(buf+bytes, (512-bytes));
		if(bytes2 > 0)
			bytes += bytes2;
	}
	current_block.resize(bytes, 0);
	memcpy(current_block.data(), buf, bytes);
}

void FileTransfer::send_next_block() {
	DATA_Packet pkt(DATA, block_no, current_block);
	Server::send(sock, &client, pkt.to_array());
}

void FileTransfer::ack(unsigned short _block_no) {
	if(_block_no == block_no) {
		if(current_block.size() == 512) {
			get_next_block();
			block_no += 1;
			send_next_block();
		}
		else {
			// Transfer is complete
			finished = true;
			file.close();
			close(sock);
		}
	}
	else if(_block_no == block_no-1){
		send_next_block();
	}
}

void FileTransfer::send_file_not_found() {
	ERROR_Packet epkt(ERROR, FILENOTFOUND, get_error());
	Server::send(sock, &client, epkt.to_array());
	close(sock);
}

void FileTransfer::close_ft() {
	file.close();
	close(sock);
}

int Server::new_file_transfer(struct sockaddr_in* _client, const char* _filename) {
	FileTransfer * ft = new FileTransfer(_client, (server_dir+string(_filename)).c_str());
	if(ft->is_open == false){
		return 0;
	}else{
		transfers[ft->sock] = ft;
		return ft->sock;
	}
}

void Server::start_transfer(int sock) {
	transfers[sock]->send_next_block();
}

int Server::handle_transfer(int sock) {
	transfers[sock]->read();
	if(transfers[sock]->finished){
		transfers.erase(sock);
		return 1;
	}
	return 0;	
}

Packet* Server::read_packet(int _sock, struct sockaddr_in* _src) {
	std::vector<char> mem = read(_sock, _src);
	return generate_packet(mem);
}

int Server::handle_select_timout(FileTransfer* ft){
	(ft->interval)+=((tv.tv_sec)*1000);
	ft->last_time.tv_sec += tv.tv_sec;
	if((ft->interval) >= (first_interval*1000)){
		(ft->retry_time)++;
		ft->interval = 0;
		first_interval = first_interval*2;
	}
	return ft->retry_time;
}

int Server::handle_interval(FileTransfer* ft){
	gettimeofday(&(ft->current_time),NULL);
	ft->interval+=get_interval(ft->last_time, ft->current_time);
	ft->last_time.tv_sec = ft->current_time.tv_sec;
	ft->last_time.tv_usec = ft->current_time.tv_usec;
	if((ft->interval) >= (first_interval*1000)){
		(ft->retry_time)++;
		ft->interval = 0;
		first_interval = first_interval*2;
	}
	return ft->retry_time;
}

void Server::handle_server_connection(){
	int maxfdp = incoming_sock;
	int conn_number = 0;
	int new_client_fd;	
	fd_set fdsr;
	int rset;
	int fd_A[BACKLOG_MAX];
	for(int i=0; i<BACKLOG_MAX; i++){	
		fd_A[i] = 0;		
	}
	//loop
	while(1){
		rset = 0;
		FD_ZERO(&fdsr);
		FD_SET(incoming_sock, &fdsr);
		tv.tv_sec =  2;
		tv.tv_usec = 0;
		// push all connection to the fd set
		for(int i=0; i<BACKLOG_MAX; i++){
			if(fd_A[i]!=0){
				FD_SET(fd_A[i],&fdsr);
			}	
		}
		// execute select call
		rset = select(maxfdp+1, &fdsr, NULL, NULL, &tv);
		if(rset <0){
			perror("select");
			break;
		}else if(rset ==0){
			printf("timeout\n");
			for(trans_it=transfers.begin(); trans_it!=transfers.end(); trans_it++){
				if(trans_it->first!=0){
					int _retry_time = handle_select_timout(trans_it->second);
					if(_retry_time == retry_limit){
						FD_CLR(trans_it->first,&fdsr);
						for(int i=0; i<BACKLOG_MAX; i++){
							if(fd_A[i]==trans_it->first){
								fd_A[i]=0;
								break;
							}	
						}
						conn_number --;
						trans_it->second->close_ft();
						transfers.erase(trans_it->second->sock);
					}	
				}
			}
			continue;	
		}
		// check all sockfd in the fd set
		
		for(int j=0; j<BACKLOG_MAX; j++){
			if(FD_ISSET(fd_A[j],&fdsr)){
				int flag = handle_transfer(fd_A[j]);
				if(flag==1){
					FD_CLR(fd_A[j],&fdsr);
					fd_A[j]=0;
					conn_number --;
				}
				for(trans_it=transfers.begin(); trans_it!=transfers.end(); trans_it++){
					if(trans_it->first!=0 && trans_it->first!=fd_A[j]){
						int _retry_time = handle_interval(trans_it->second);
						//printf("interval[%d]=%ld\n",trans_it->first,trans_it->second->interval);
						if(_retry_time == retry_limit){
							FD_CLR(trans_it->first,&fdsr);
							for(int i=0; i<BACKLOG_MAX; i++){
								if(fd_A[i]==trans_it->first){
									fd_A[i]=0;
									break;
								}	
							}
							conn_number --;
							trans_it->second->close_ft();
							transfers.erase(trans_it->second->sock);
						}	
					}else if(flag==0){
						transfers[fd_A[j]]->interval = 0;
						transfers[fd_A[j]]->retry_time = 0;
					}
				}
			}
		}
		//check RRQ
		if(FD_ISSET(incoming_sock, &fdsr)){
			struct sockaddr_in* _client= new struct sockaddr_in;
			Packet * pkt = read_packet(incoming_sock, _client);
			if(pkt->get_opcode() == RRQ) {
				RRQ_Packet * rp = (RRQ_Packet *) pkt;
				printf("Received RRQ file: %s\n", rp->get_filename().c_str());
				new_client_fd = new_file_transfer(_client, rp->get_filename().c_str());
				if(new_client_fd == 0){
					continue;
				}
				start_transfer(new_client_fd);
			}
			printf("new_client_fd: [%d]\n", new_client_fd);
			if(new_client_fd <= 0){
				perror("new client");
				continue;
			}	
			//handle the new client
			if(conn_number<BACKLOG_MAX){
				for(int n=0;n<BACKLOG_MAX;n++){
					if(fd_A[n]==0){
						fd_A[n] = new_client_fd;
						break;				
					}				
				}
				conn_number++;
				if(new_client_fd>maxfdp){
					maxfdp = new_client_fd;		
				}	
				
			}
			for(trans_it=transfers.begin(); trans_it!=transfers.end(); trans_it++){
				if(trans_it->first!=0 && trans_it->first!=new_client_fd){
					int _retry_time = handle_interval(trans_it->second);
					if(_retry_time == retry_limit){
						FD_CLR(trans_it->first,&fdsr);
						for(int i=0; i<BACKLOG_MAX; i++){
							if(fd_A[i]==trans_it->first){
								fd_A[i]=0;
								break;
							}	
						}
						conn_number --;
						trans_it->second->close_ft();
						transfers.erase(trans_it->second->sock);
					}	
				}
			}
		}//check RRQ
	}	
}
